package roborally.board;

import roborally.itemsonboard.BoardItem;
import roborally.itemsonboard.EnergyItem;
import roborally.supporting.PhysicalAmount;
import roborally.supporting.Position;
import roborally.supporting.Unit;

/**
 * A class, collection some defaultOptions for selecting a particular subrange of items of a board.
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen, Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 3.0
 *
 */
public class Option {
	
	/**
	 * Returns a SelectionOption which only allows boardItems which extends EnergyItem and
	 * have at least the given physicalAmount as energy.
	 * 
	 * @param physicalAmount The minimal energy the boardItems must have.
	 * @pre The unit of the physicalAmount must be convertible to Unit.WATTSECOND
	 * 		| physicalAmount.getUnit().canConvertTo(Unit.WATTSECOND)
	 * @return
	 * 		| SelectionOption.class.isInstance(result)
	 * 		| let object be a random BoardItem (or null)
	 * 		| if !EnergyItem.class.isInstance(object)
	 * 		|	then result.satisfy(object) == false
	 * 		| else if physicalAmount.compareTo(EnergyItem.class.cast(object).getEnergyLevel()) > 0
	 * 		|	then result.satisfy(object) == false
	 * 		| else
	 * 		|	then result.satisfy(object) == true
	 * 
	 */
	public static SelectionOption minimalEnergy(final PhysicalAmount physicalAmount) {
		assert physicalAmount.getUnit().canConvertTo(Unit.WATTSECOND);
		return new SelectionOption() {

			/**
			 * Controls if the given boardItem satisfies some predefined option.
			 * 
			 * @param boardItem The boardItem to control.
			 * @return False if the boardItem is null (repeated)
			 * 		| if boardItem == null
			 * 		|	then result == false
			 * @return False if the boardItem is not a sub type of EnergyItem
			 * 		| if !EnergyItem.class.isInstance(BoardItem)
			 * 		|	then result == false
			 * @return False if boardItem does not have the necessary energy
			 * 		| if physicalAmount.compareTo(EnergyItem.class.cast(boardItem).getEnergyLevel()) > 0
			 * 		|	then result == false
			 * @return True in any other case.
			 * 		| result == true
			 */
			@Override
			public boolean satisfy(BoardItem boardItem) {
				if(boardItem == null)
					return false;
				else if(!EnergyItem.class.isInstance(boardItem))
					return false;
				else if(physicalAmount.compareTo(EnergyItem.class.cast(boardItem).getEnergyLevel()) > 0)
					return false;
				else
					return true;
			}
			
		};
	}
	
	/**
	 * Returns a SelectionOption which only allows boardItems that are located on a position
	 * smaller then the given one.
	 * 
	 * @param smallest The smallest position of an item that will be accepted.
	 * @param largest The largest position of an item that will be accepted
	 * @pre
	 * 		| smallest != null
	 * @pre 
	 * 		| largest != null
	 * @return
	 * 		| SelectionOption.class.isInstance(result)
	 * 		| let object be a random BoardItem (or null)
	 * 		| if object == null
	 * 		|	then result.satisfy(object) == false
	 * 		| else if BoardItem.class.cast(object).getPosition().getX() >= smallest.getX()
	 * 		|			&& BoardItem.class.cast(object).getPosition().getX() <= largest.getX()
	 * 		|			&& BoardItem.class.cast(object).getPosition().getY() >= smallest.getY()
	 * 		|			&& BoardItem.class.cast(object).getPosition().getY() <= largest.getY()
	 * 		|	then result == true
	 * 		| else
	 * 		|	then result == false
	 */
	public static SelectionOption subRange(final Position smallest, final Position largest) {
		assert smallest != null;
		assert largest != null;
		return new SelectionOption() {
			
			/**
			 * Controls if the given boardItem satisfies some predefined option.
			 * 
			 * @param boardItem The boardItem to control.
			 * @return False if the boardItem is null (repeated)
			 * 		| if boardItem == null
			 * 		|	then result == false
			 * @return True if the object's position lies in the rectangle determined by smallest and largest.
			 * 		| if BoardItem.class.cast(object).getPosition().getX() >= smallest.getX()
			 * 		|		&& BoardItem.class.cast(object).getPosition().getX() <= largest.getX()
			 * 		|		&& BoardItem.class.cast(object).getPosition().getY() >= smallest.getY()
			 * 		|		&& BoardItem.class.cast(object).getPosition().getY() <= largest.getY()
			 * 		|	then result == true
			 * @return False in any other case.
			 * 		| false
			 */
			@Override
			public boolean satisfy(BoardItem boardItem) {
				if(boardItem == null)
					return false;
				else if(boardItem.getPosition().getX() >= smallest.getX() && 
						boardItem.getPosition().getX() <= largest.getX() &&
						boardItem.getPosition().getY() >= smallest.getY() &&
						boardItem.getPosition().getY() <= largest.getY() )
					return true;
				else
					return false;
			}
		};
	}
	
	/**
	 * Every boardItem but null will be accepted by the SelectionOption that will be returned.
	 * 
	 * @return
	 * 		| SelectionOption.class.isInstance(result)
	 * 		| let object be a random BoardItem (or null)
	 * 		| result.satisfy(object) == (object != null)
	 */
	public static SelectionOption none() {
		return new SelectionOption() {
			
			/**
			 * Controls if the given boardItem satisfies some predefined option.
			 * 
			 * @param boardItem The boardItem to control.
			 * @return False if the boardItem is null (repeated)
			 * 		| if boardItem == null
			 * 		|	then result == false
			 * @return True in any other case.
			 * 		| true
			 */
			@Override
			public boolean satisfy(BoardItem boardItem) {
				return boardItem != null; // according to the Interface, null always should return false.
			}
		};
	}
	
	/**
	 * No object can be created of this class.
	 */
	private Option() {
	}
}
